#include "helper.h"
#include "all-instr.h"
#include "monitor.h"

typedef void (*op_fun)(uint32_t);
static make_helper(_2byte_esc);
static make_helper(bgezs); // 分支指令也需要进一步判断
static make_helper(privilege_instr);

Operands ops_decoded;
uint32_t instr;

/* TODO: Add more instructions!!! */

op_fun opcode_table[64] = {
	/* 0x00 */ _2byte_esc, bgezs, j, jal,
	/* 0x04 */ beq, bne, blez, bgtz,
	/* 0x08 */ addi, addiu, slti, sltiu,
	/* 0x0c */ andi, ori, xori, lui,
	/* 0x10 */ privilege_instr, inv, temu_trap, inv,
	/* 0x14 */ inv, inv, inv, inv,
	/* 0x18 */ inv, inv, inv, inv,
	/* 0x1c */ inv, inv, inv, inv,
	/* 0x20 */ lb, lh, inv, lw,
	/* 0x24 */ lbu, lhu, inv, inv,
	/* 0x28 */ sb, sh, inv, sw,
	/* 0x2c */ inv, inv, inv, inv,
	/* 0x30 */ inv, inv, inv, inv,
	/* 0x34 */ inv, inv, inv, inv,
	/* 0x38 */ inv, inv, inv, inv,
	/* 0x3c */ inv, inv, inv, inv};

op_fun _2byte_opcode_table[64] = {
	/* 0x00 */ sll, inv, srl, sra,
	/* 0x04 */ sllv, inv, srlv, srav,
	/* 0x08 */ jr, jalr, inv, inv,
	/* 0x0c */ syscall, break_, inv, inv,
	/* 0x10 */ mfhi, mthi, mflo, mtlo,
	/* 0x14 */ inv, inv, inv, inv,
	/* 0x18 */ mult, multu, div, divu,
	/* 0x1c */ inv, inv, inv, inv,
	/* 0x20 */ add, addu, sub, subu,
	/* 0x24 */ and, or, xor, nor,
	/* 0x28 */ inv, inv, slt, sltu,
	/* 0x2c */ inv, inv, inv, inv,
	/* 0x30 */ inv, inv, inv, inv,
	/* 0x34 */ inv, inv, inv, inv,
	/* 0x38 */ inv, inv, inv, inv,
	/* 0x3c */ inv, inv, inv, bad_temu_trap};

make_helper(exec)
{
	instr = instr_fetch(pc, 4);
	ops_decoded.opcode = instr >> 26;
	opcode_table[ops_decoded.opcode](pc);
}

static make_helper(_2byte_esc)
{
	ops_decoded.func = instr & FUNC_MASK;
	_2byte_opcode_table[ops_decoded.func](pc);
}

static make_helper(bgezs)
{
	uint32_t select = (instr & 0x001F0000) >> 16;
	switch (select)
	{
	case 0:
		bltz(pc);
		break;
	case 1:
		bgez(pc);
		break;
	case 16:
		bltzal(pc);
		break;
	case 17:
		bgezal(pc);
		break;
	}
}

static make_helper(privilege_instr){
	uint32_t mark = (instr >> 21) & 0x001F;
	switch (mark)
	{
	case 0 :
		mfc0(pc);break;
	case 4 :
		mtc0(pc); break;
	case 16 : 
		eret(pc); break;
	}
}